%{
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include "symtab.h"
#include "codegen.h"
#include "utils.h"
  extern int lexeme, line; 
  int argum = 0;
%}

%union {
  int intval;
  char *name;
  char *code;
  struct arguments *args;
}

%type <code> expr instruction l_instructions corps fonction l_fonctions prototype debut en_tete suite_argum liste_argum
%type <args> liste_types liste_param param

%token INT STATIC VOID
%token IF WHILE ELSE RETURN EXIT READ
%token <intval> ENTIER
%token <name> ID
%token END 0 "end of file"

%left ','
%right '='
%left OU
%left ET
%left EGAL DIF 
%left '<' '>' INF_EG SUP_EG
%left '+' '-'
%left '*' '/' '%' 
%right '!' MOINS_UNAIRE
%left '(' '['

%expect 1 // Conflit if-else : décalage accepté par défaut

%error-verbose

%%

prog : debut { dump($1); }
;

debut : declaration debut { $$ = $2; }
      | error debut { yyerrok; $$ = $2; }
      | fonction l_fonctions { $$ = concat($1, $2, NULL); }
;

declaration : INT ID ';' { add_variable($2, false); }
            | INT ID '[' ENTIER ']' ';' { add_array($2, $4, false); } 
            | STATIC INT ID ';' { add_variable($3, true); }
            | STATIC INT ID '[' ENTIER ']' ';' { add_array($3, $5, true); }
            | prototype ';' {  }
;

prototype : VOID ID '(' VOID ')' { add_prototype(RET_VOID, $2, NULL); } 
          | VOID ID '(' liste_types ')' { add_prototype(RET_VOID, $2, $4); } 
          | INT ID '(' VOID ')' { add_prototype(RET_INT, $2, NULL); } 
          | INT ID '(' liste_types ')' { add_prototype(RET_INT, $2, $4); } 
;

liste_types : INT { struct arguments *arg; $$ = arg; }
            | INT ',' liste_types { struct arguments *arg; $$ = arg; }
;

fonction : en_tete '{' corps '}' { /* display_symtab(); // Peut être testé */
                                   $$ = gen_func($1, $3); fin_local(); }
         | error '{' corps '}' { yyerrok; $$ = $3; }
;

l_fonctions : /* Empty */ { $$ = ""; }
            | fonction l_fonctions { $$ = concat($1, $2, NULL); }
;

en_tete : VOID ID '(' VOID ')' { $$ = gen_entete(RET_VOID, $2, NULL); }
        | VOID ID '(' liste_param ')' { $$ = gen_entete(RET_VOID, $2, $4); }
        | INT ID '(' VOID ')' { $$ = gen_entete(RET_VOID, $2, NULL); }
        | INT ID '(' liste_param ')' { $$ = gen_entete(RET_INT, $2, $4); }
;

liste_param : param { struct arguments *arg; $$ = arg; }
            | param ',' liste_param { struct arguments *arg; $$ = arg; }
;

param : INT ID { struct arguments *arg; $$ = arg; }
      | INT ID '[' ENTIER ']' { struct arguments *arg; $$ = arg; }
;

corps : declaration corps { $$ = $2; }
      | error declaration corps { yyerrok; $$ = $3; }
|	l_instructions { $$ = $1; }
;

l_instructions : /* Empty */ { $$ = ""; }
               | instruction l_instructions { $$ = concat($1, $2, NULL); }
               | error l_instructions { yyerrok; $$ = $2; }
;

instruction : ID '=' expr ';' { $$ = gen_assign($1, $3); }
            | ID '[' expr ']' '=' expr ';'{ $$ = "a"; }
            | EXIT '(' ENTIER ')' ';'{ $$ = gen_exit($3); }
            | RETURN expr ';'{ $$ = gen_return_exp($2); }
            | RETURN ';'{ $$ = gen_return(); }
            | READ '(' ID ')' ';'{ $$ = "a"; }
            | IF '(' expr ')' instruction { $$ = gen_if($3, $5); }
            | IF '(' expr ')' instruction ELSE instruction { $$ = gen_if_else($3, $5, $7); }
            | WHILE '(' expr ')' instruction{ $$ = gen_while($3, $5); }
            | '{' l_instructions '}'{ $$ = $2; }
            | expr ';' { $$ = $1; }
;

expr : expr '+' expr { $$ = gen_expr(PLUS, $1, $3); }
     | expr '-' expr { $$ = gen_expr(MOINS, $1, $3); }
     | expr '*' expr { $$ = gen_expr(FOIS, $1, $3); }
     | expr '/' expr { $$ = gen_expr(DIVISE, $1, $3); }
     | expr '%' expr { $$ = gen_expr(MODULO, $1, $3); }
     | expr '<' expr { $$ = gen_expr(INF, $1, $3); }
     | expr '>' expr { $$ = gen_expr(SUP, $1, $3); }
     | expr INF_EG expr { $$ = gen_expr(INF_EG, $1, $3); }
     | expr SUP_EG expr { $$ = gen_expr(SUP_EG, $1, $3); }
     | expr DIF expr { $$ = gen_expr(DIF, $1, $3); }
     | expr EGAL expr { $$ = gen_expr(EGAL, $1, $3); }
     | expr ET expr { $$ = gen_expr(ET, $1, $3); }
     | expr OU expr { $$ = gen_expr(OU, $1, $3); }
     | '-' expr %prec MOINS_UNAIRE { $$ = gen_expr(MOINS_UNAIRE, $2, NULL); }
     | '!' expr { $$ = gen_expr(NOT, $2, NULL); }
     | '(' expr ')' { $$ = $2; }
     | ID { $$ = gen_term($1); }
     | ID '(' suite_argum ')' { $$ = gen_call($1, $3); argum=0; }
     | ID '[' expr ']' { $$ = "a"; }
     | ENTIER { char entier[10]; sprintf(entier, "%d", $1); 
                $$ = concat("\tpushl\t$", entier, "\n", NULL); }
;

suite_argum : /* Empty */ { $$ = concat("", NULL); }
            | liste_argum { $$ = $1; }
;

liste_argum : expr { $$ = concat($1, "\tpushl\t%ebp\n", NULL); argum ++; }
            | expr ',' liste_argum { $$ = concat($1, $3, NULL); argum ++;}
;

%%

yyerror(char *message)
{
  printf("\n******** ERROR ********\n\nLigne %d, lexeme %d :\n%s\n", line, lexeme, message);
}

int main(int argc, char *argv[])
{
  creer_dico();
  //yydebug = 1;
  if(yyparse() == 0)
    printf("\nAnalyse terminee\n");
  return(0);
}
 
